package utils

import (
	"strconv"
	"strings"
	"sync"
)

type CfgMap struct {
	kv map[string]string
	l  sync.RWMutex
}

var cfgMap *CfgMap

func init() {
	cfgMap = &CfgMap{
		kv: make(map[string]string),
	}
	_ = CfgReload()
}

// CfgReload 加载（重新加载）配置数据
func CfgReload(envPaths ...string) error {
	cfgMap.l.Lock()
	defer cfgMap.l.Unlock()
	if len(envPaths) == 0 {
		defaultEnv := ""
		if binPath, err := GetBinPath(); err != nil {
			defaultEnv = "./.env"
		} else if defaultEnv = strings.TrimRight(binPath, "/") + "/.env"; !FileExist(defaultEnv) {
			defaultEnv = "./.env"
		}
		envPaths = append(envPaths, defaultEnv)
	}
	kv := make(map[string]string)
	for _, path := range envPaths {
		if lines, err := FileReadLines(path); err == nil {
			for _, line := range lines {
				line = StrTrim(line)
				if line == "" {
					continue
				}
				if p := strings.Index(line, "="); p <= 0 {
					continue
				} else {
					kv[line[0:p]] = line[p+1:]
				}
			}
		} else {
			return err
		}
	}
	cfgMap.kv = kv
	return nil
}

// CfgGetStr 按 key 取 配置内容
func CfgGetStr(key string) (string, bool) {
	if key = StrTrim(key); key == "" {
		return "", false
	}
	cfgMap.l.RLock()
	defer cfgMap.l.RUnlock()
	val, exist := cfgMap.kv[key]
	return val, exist
}

// CfgGetStrArr 按 key 取 配置内容，结果转化为 []string
func CfgGetStrArr(key string) ([]string, bool) {
	if val, exist := CfgGetStr(key); !exist {
		return nil, false
	} else {
		return strings.Split(val, ","), true
	}
}

// CfgGetInt 按 key 取 配置内容，结果转化为 INT
func CfgGetInt(key string) (int, bool) {
	val, exist := CfgGetStr(key)
	if !exist {
		return 0, false
	}
	i, err := strconv.Atoi(val)
	if err != nil {
		return 0, false
	}
	return i, true
}

// CfgGetFloat 按 key 取配置内容，结果转化为 float
func CfgGetFloat(key string) (float64, bool) {
	val, exist := CfgGetStr(key)
	if !exist {
		return 0, false
	}
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return 0, false
	}
	return f, true
}

// CfgGetBool 按 key 取 配置内容，结果转化为 BOOL
func CfgGetBool(key string) (bool, bool) {
	val, exist := CfgGetStr(key)
	if !exist {
		return false, false
	}
	valStr := strings.ToLower(val)
	valInt, err := strconv.Atoi(valStr)
	switch {
	case valStr == "true":
		fallthrough
	case err == nil && valInt > 0:
		return true, true
	case valStr == "false":
		fallthrough
	case err == nil && valInt <= 0:
		return false, true
	default:
		return false, true
	}
}
